package com.amkai.aes.webservice.binary.objecthandler.billing;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.amkai.aes.aescommon.exceptions.AOException;
import com.amkai.aes.aescommon.service.binary.job.billing.BillingJob;
import com.amkai.aes.aescommon.service.result.billing.BillingResult;
import com.amkai.aes.generatedbeans.bill.configuration.TMDepartmentConfiguration;
import com.amkai.aes.generatedbeans.bill.configuration.TMSupplyResourceMarkupRate;
import com.amkai.aes.generatedbeans.bill.configuration.TimeAndMaterialConfiguration;
import com.amkai.aes.generatedbeans.organization.Department;
import com.amkai.aes.server.billing.BillingCalculationProviderImpl;
import com.amkai.aes.webservice.binary.JobObjectHandler;
import com.amkai.common.extensions.hibernate.config.SiteManager;
import com.amkai.common.extensions.hibernate.config.manager.SessionManager;
import com.amkai.rad.designerbase.beans.ChangedKind;

public class BillingHandler implements JobObjectHandler<BillingJob, Department, BillingResult> {

	private static final Log log = LogFactory.getLog(BillingHandler.class);
	private static final String TITLE = "Billing";
	
	@Override
	public BillingResult process(BillingJob job, Department dep) throws Exception {
		switch (job.getKind()) {
		case GET_PROCEDURE_COUNT_BY_CASE:	return getProcedureCountByCase(job, dep);
		//#14739
		case LOAD_TIME_MATERIAL_CONFIGURATION: return loadTimeAndMaterialConfiguration(job);
		//#14739
		case SAVE_TIME_MATERIAL_CONFIGURATION: return saveTimeAndMaterialConfiguration(job);
		case GET_AMOUNT_BY_SUPPLY_LOGS:	return getAmountBySupplyLogs(job);
		case GET_TYPE_OB_BILL:	return getTypeOfBill(job);
		}
		throw new AOException(TITLE, "Unknown JobKind", job.getKind().toString());
	}
	
	private BillingResult getProcedureCountByCase(BillingJob job, Department moduleDepartment) throws Exception {
		BillingResult result = new BillingResult();
		SessionManager sm = SiteManager.Start();
		try {
			result.setProcedureCount(BillingCalculationProviderImpl.getProcedureCountByCase(sm, job.getPmCase()));
		}
		catch (Exception ex) {
			result.setException(ex);
			logError(ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}

	/**
	 * #14739
	 * @param job
	 * @return
	 * @throws Exception
	 */
	private BillingResult loadTimeAndMaterialConfiguration(BillingJob job) throws Exception {
		BillingResult result = new BillingResult();
		SessionManager sm = SiteManager.Start();
		try {
			BillingCalculationProviderImpl provider = new BillingCalculationProviderImpl(sm, job.getDepartment());
			TimeAndMaterialConfiguration configuration = provider.getTimeAndMaterialConfiguration(job.getDepartment());
			for (TMDepartmentConfiguration c : configuration.getDepartmentConfigurations()) {
				if (c.getRevenueCode() != null)
					c.getRevenueCode().getShortName();
				if (c.getBillingRate() != null)
					c.getBillingRate().getShortName();
			}
			for (TMSupplyResourceMarkupRate r : configuration.getSupplyResourceMarkupRates()) {
				r.getAmountThrough();
			}
			result.setTimeAndMaterialConfiguration(configuration);
		}
		catch (Exception ex) {
			result.setException(ex);
			logError(ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}

	/**
	 * #14739
	 * @param job
	 * @return
	 * @throws Exception
	 */
	private BillingResult saveTimeAndMaterialConfiguration(BillingJob job) throws Exception {
		BillingResult result = new BillingResult();
		SessionManager sm = SiteManager.Start();
		try {
			sm.openTransaction();
			sm.saveRAD(job.getTimeAndMaterialConfiguration());
			sm.execute("DELETE FROM TMDepartmentConfiguration c WHERE c.timeAndMaterialConfiguration=:c").setParameter("c", job.getTimeAndMaterialConfiguration()).executeUpdate();
			for (TMDepartmentConfiguration c : job.getTimeAndMaterialConfiguration().getDepartmentConfigurations()) {
				c.setId(null);
				c.setChangedKind(ChangedKind.CREATED);
				c.setTimeAndMaterialConfiguration(job.getTimeAndMaterialConfiguration());
				sm.saveRAD(c);
			}
			sm.execute("DELETE FROM TMSupplyResourceMarkupRate r WHERE r.timeAndMaterialConfiguration=:c").setParameter("c", job.getTimeAndMaterialConfiguration()).executeUpdate();
			for (TMSupplyResourceMarkupRate r : job.getTimeAndMaterialConfiguration().getSupplyResourceMarkupRates()) {
				r.setId(null);
				r.setChangedKind(ChangedKind.CREATED);
				r.setTimeAndMaterialConfiguration(job.getTimeAndMaterialConfiguration());
				sm.saveRAD(r);
			}
			result.setTimeAndMaterialConfiguration(job.getTimeAndMaterialConfiguration());
			sm.commitTransaction();
		}
		catch (Exception ex) {
			sm.rollbackTransaction();
			result.setException(ex);
			logError(ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	private BillingResult getAmountBySupplyLogs(BillingJob job) throws Exception {
		BillingResult result = new BillingResult();
		SessionManager sm = SiteManager.Start();
		try {
			//AES-14638
			Department department = sm.get(Department.class, job.getModuleDepartment().getId());
			BillingCalculationProviderImpl provider = new BillingCalculationProviderImpl(sm, department);
			result.setAmount(provider.calculateAmountBySupplyLogs(job.getPmCase(), job.getSupplyResourceItem(), job.getUnits()));
		}
		catch (Exception ex) {
			result.setException(ex);
			logError(ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	private BillingResult getTypeOfBill(BillingJob job) throws Exception {

		BillingResult result = new BillingResult();
		SessionManager sm = SiteManager.Start();
		try {
			String typeOfBill = null;
			List<String> l = sm.execute("SELECT c.typeOfBill " +
					"FROM Patient p " +
					"LEFT JOIN p.entries e, Charge c " +
					"WHERE c.typeOfBill IS NOT NULL " +
					"AND c.id=e.id " +
					"AND p.id=:patientID " +
					"ORDER BY e.lastEditDate DESC")
						.setLong("patientID", job.getPatientID())
						.list();
			if (l.size() > 0) {
				typeOfBill = l.get(0);
			}
			result.setTypeOfBill(typeOfBill);
		}
		catch (Exception ex) {
			logError(ex);
			result.setException(ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}

	private void logError(Exception ex) {
		if (!(ex instanceof AOException))
			log.error(TITLE, ex);
	}
}
